package com.kehutong.payfees.service;

import com.kehutong.common.DoveClient;
import com.kehutong.common.ExcelHeaders;
import com.kehutong.common.Java8TimeUtils;
import com.kehutong.common.math.CalculatorUtils;
import com.kehutong.common.util.Token;
import com.kehutong.payfees.dto.WebChat;
import com.kehutong.payfees.entity.ChargeBill;
import com.kehutong.payfees.entity.record.ChargeRecord;
import com.kehutong.payfees.entity.record.ChargeRecordRel;
import com.kehutong.payfees.enums.*;
import com.kehutong.payfees.service.receipt.ReceiptOrderServiceImpl;
import com.kehutong.payfees.util.BasicClient;
import com.kehutong.payfees.util.ExcelUtil;
import com.kehutong.payfees.util.JDBCUtil;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.coraframework.converter.Converter;
import org.coraframework.converter.util.StringUtil;
import org.coraframework.inject.Inject;
import org.coraframework.json.JSONArray;
import org.coraframework.json.JSONObject;
import org.coraframework.logger.Logger;
import org.coraframework.logger.LoggerFactory;
import org.coraframework.mvc.http.result.Result;
import org.coraframework.mvc.webservice.WebService;
import org.coraframework.orm.Pool;
import org.coraframework.orm.jdbc.JdbcSession;
import org.coraframework.orm.jdbc.execute.entity.FindArray;
import org.coraframework.threadpool.ThreadPool;
import org.coraframework.util.Objects;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Author:  peter
 * Date:    2020/7/10
 * Function:缴费任务
 */
@WebService("/payfees/charge/bill")
public class ChargeBillServiceImpl {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Inject
    private BasicClient basicClient;
    @Inject
    protected JdbcSession jdbcSession;
    @Inject
    protected ThreadPool threadPool;
    @Inject
    private ChargeLogServiceImpl chargeLogService;
    @Inject
    private ChargeBillAnnexServiceImpl chargeBillAnnexService;

    @Inject
    private ChargeBillServiceImpl chargeBillService;

    @Inject
    private DoveClient doveClient;

    @Inject
    private ReceiptOrderServiceImpl receiptOrderServiceImpl;


    public void handleBillByPaidChargeRecord(String companyNo, ChargeRecord cr, String attch) {
        JSONObject jsonObject = JSONObject.parseObject(attch);

        List<ChargeRecordRel> rels = jdbcSession.findArray(ChargeRecordRel.class)
                .eq("chargeRecordId", cr.getId())
                .eq("type", ChargeRecordRelType.CHARGE_BILL)
                .exe();

        StringBuffer sb = new StringBuffer();
        List<ChargeBill> bills = new ArrayList<>();
        for (ChargeRecordRel rel : rels) {
            ChargeBill bill = Pool.get(ChargeBill.class, rel.getExternalId());
            if (bill == null) {
                logger.error("can't find bill with id: {}", rel.getExternalId());
                continue;
            }

            if (bill.getPaidState() != BillPaidState.UNPAID) {
                continue;
            }

            bill.setPaidAt(LocalDateTime.now());
            bill.setPayMethod(BillPayMethod.WECHAT_PAY);
            bill.setPaidState(BillPaidState.PAID);

            // 获取付款人信息
            bill.setPayerNo(cr.getMbrCode());
            bill.setPayerName(cr.getMbrName());
            bill.setPayerMobile(cr.getMobile());

            jdbcSession.updateById(bill);
            sb.append(bill.getId()).append(",");

            bills.add(bill);

            try {
                //生成票据
                receiptOrderServiceImpl.savePdf(bill, companyNo);
            }catch (Exception e){
                logger.error("账单：{},生成收据PDF文件失败！",bill.getId(),e);
            }
        }

        // 支付总金额
        BigDecimal sumTotal = chargeBillService.sumTotal(bills);

        if(Objects.nonEmpty(bills)) {
            String houseNo = bills.get(0).getHouseNo();

            //异步执行 通过合并状态区分 是合并支付还是根据订单进行支付发送支付成功的通知信息 通知缴费人
            Boolean isMerge = jsonObject.getBoolean("merge");
            if(isMerge){
                asyncNotifyCustomAfterPaySuccess(companyNo, houseNo, sumTotal, cr , null);
            }else{
                asyncNotifyCustomAfterPaySuccess(companyNo, houseNo, sumTotal, cr , bills.get(0));
            }
        }

    }

    public void asyncNotifyCustomAfterPaySuccess(String companyNo, String houseNo, BigDecimal sumTotal, ChargeRecord cr, ChargeBill bill){
        threadPool.execute(() -> {
            try {
                JSONObject result = doveClient.post("/crm/resident/house/service/getHouseOwner", http->{
                    http.addHeader("companyNo", companyNo);
                    http.addParam("houseNo", houseNo);
                });
                int resultCode = result.getIntValue("code");
                if (resultCode == 200) {
                    JSONObject houseOwner = result.getJSONObject("data");
                    if (Objects.nonEmpty(houseOwner)) {
                        JSONObject rs = doveClient.post("/kht-bin/wechat/getMemberMap", http -> {
                            http.addParam("khtId",houseOwner.getString("residentNo"));
                            http.addParam("companyNo",companyNo);
                        });
                        int code = rs.getIntValue("code");
                        if (code != 200) {
                            logger.warn("失败：" + rs.getString("message"));
                            return;
                        }
                        JSONObject memberMap = rs.getJSONObject("data");
                        if(Objects.isEmpty(memberMap)){
                            logger.warn("手机号" + houseOwner.getString("mobile") + "的用户没有关注过公众号");
                            return;
                        }

                        String openId =memberMap.getString("mp_open_id");
                        if(Objects.isBlank(openId)){
                            logger.warn("手机号" + houseOwner.getString("mobile") + "的用户openId不存在");
                            return;
                        }

                        JSONObject data = new JSONObject();
                        data.put("first", buildMpMessageData("您好，您已缴费成功。"));
                        // 缴费地址
                        data.put("keyword1", buildMpMessageData(String.format(getFullNameFromHouse(companyNo, houseOwner))));
                        // 交易单号
                        data.put("keyword2", buildMpMessageData(cr.getTransactionId()));
                        // 支付金额
                        data.put("keyword3", buildMpMessageData(CalculatorUtils.formatMoneyString(sumTotal) + "元"));
                        // 支付方式
                        data.put("keyword4", buildMpMessageData(cr.getBizType().getDesc()));
                        // 支付时间
                        data.put("keyword5", buildMpMessageData(Java8TimeUtils.parseString(cr.getUpdateTime().toLocalDateTime())));

                        data.put("remark", buildMpMessageData("感谢您的支持"));

                        String msg;
                        JSONObject resultSend = doveClient.post("/message/mini/uniformSend", (http) -> {
                            http.addParam("openid", openId);
                            http.addParam("templateType", "PAY_SUCCESS");
                            if(Objects.nonNull(bill)){
                                http.addParam("param", getParam("billNo", bill.getId(), "companyNo", companyNo, "houseNo", houseNo));
                            }else{
                                http.addParam("param", getParam("orderNo", cr.getId(), "companyNo", companyNo, "houseNo", houseNo));
                            }
                            JSONObject body = new JSONObject();
                            body.put("data", data);
                            http.setBody(body.toString());
                        });
                        int codeSend = resultSend.getIntValue("code");
                        if (codeSend == 200) {
                            logger.info("手机号"+houseOwner.getString("mobile")+"的用户已经成功缴费用");
                        }else{
                            msg = resultSend.getString("message");
                            logger.warn(msg);
                        }
                    }
                }
            }catch (Exception e){
                logger.error(e);
            }
        });
    }

    private String getParam(String...params) {
        if (params.length % 2 != 0) {
            throw new IllegalArgumentException("参数必须是偶数!");
        }
        StringBuilder buf = new StringBuilder();

        for (int i = 0; i < params.length; i += 2) {
            buf.append(params[i]).append("=").append(params[i + 1]).append('&');
        }

        int lastIndex = buf.length() - 1;
        if (buf.charAt(lastIndex) == '&') {
            buf.deleteCharAt(lastIndex);
        }

        return buf.toString();
    }


    private JSONObject buildMpMessageData(String value) {
        JSONObject json = new JSONObject();
        json.put("value", value);
        return json;
    }

    public String getFullNameFromHouse(String companyNo, JSONObject json) throws Exception {
        if (json == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder();

        JSONObject community = basicClient.getCommunityById(companyNo, json.getString("communityNo"));
        if (Objects.nonNull(community)) {
            sb.append(community.getString("name"));
        }

        sb.append(json.getString("buildName"));
        sb.append(json.getString("unitName"));
        sb.append(json.getString("floorName"));
        sb.append(json.getString("houseName"));

        return sb.toString();
    }

    public XSSFWorkbook generateWorkBook(List<ChargeBill> bills) {
        // 生成模板文件
        XSSFWorkbook wb = new XSSFWorkbook();
        //创建工具
        CreationHelper helper = wb.getCreationHelper();
        //单元格样式类
        CellStyle dateCellStyle = wb.createCellStyle();
        //设置单元格格式
        dateCellStyle.setDataFormat(helper.createDataFormat().getFormat("yyyy-MM-dd"));


        Sheet sheet = wb.createSheet();

        Row headerRow = sheet.createRow(0);
        for (int i = 0; i < ExcelHeaders.CHARGE_BILL_EXPORT_EXCEL_HEADER.length; i++) {
            headerRow.createCell(i).setCellValue(ExcelHeaders.CHARGE_BILL_EXPORT_EXCEL_HEADER[i]);
        }

        int cellNum = 0;
        for (int i = 0; i < bills.size(); i++) {
            cellNum = 0;
            ChargeBill bill = bills.get(i);
            Row row = sheet.createRow(i + 1);
            row.createCell(cellNum++).setCellValue(bill.getCommunityName());
            row.createCell(cellNum++).setCellValue(bill.getBuildingName());
            row.createCell(cellNum++).setCellValue(bill.getUnitName());
            row.createCell(cellNum++).setCellValue(bill.getFloorName());
            row.createCell(cellNum++).setCellValue(bill.getHouseName());

            Cell dateCell = row.createCell(cellNum++);
            dateCell.setCellStyle(dateCellStyle);
            dateCell.setCellValue(Converter.toDate(bill.getBillDateFrom()));

            dateCell = row.createCell(cellNum++);
            dateCell.setCellStyle(dateCellStyle);
            dateCell.setCellValue(Converter.toDate(bill.getBillDateTo()));

            dateCell = row.createCell(cellNum++);
            dateCell.setCellStyle(dateCellStyle);
            dateCell.setCellValue(Converter.toDate(bill.getShouldPayDate()));

            row.createCell(cellNum++).setCellValue(bill.getTotal().doubleValue());
            row.createCell(cellNum++).setCellValue(bill.getEnableState().getDesc());
            row.createCell(cellNum++).setCellValue(bill.getPaidState().getDesc());
        }

        // 自动列宽
        for (int i = 0; i < cellNum; i++) {
            ExcelUtil.autoSizeColumn(sheet, i);
        }
        return wb;
    }

    /**
     * 根据请求筛选条件
     * 统计缴费率倒序列表
     */
    public List<JSONObject> listPayRatioDescData(Token token, JSONObject req) throws Exception {

        // 组织架构条件
        List<String> departmentIds = basicClient.getDepartmentIdsFilterList(token, req.getString("departmentId"));
        if (Objects.nonNull(departmentIds)) {
            req.put("departmentIds", departmentIds);
        }

        JSONArray communitys = basicClient.getCommunitys(token, req);
        // 对每个小区独立统计
        return communitys.stream()
                .map(item -> {
                    JSONObject obj = (JSONObject) item;
                    FindArray<ChargeBill> find = jdbcSession.findArray(ChargeBill.class)
                            .eq("communityNo", obj.getString("id"))
                            .eq("enableState", BillEnableState.ENABLE)
                            .eq("deleted", false);

                    JDBCUtil.between(req, find, "shouldPayDate");
                    List<ChargeBill> bills = find.exe();

                    BigDecimal shouldReceive = sumTotal(bills);
                    BigDecimal received = sumPaidTotal(bills);
                    BigDecimal toReceive = sumUnPaidTotal(bills);
                    JSONObject r = new JSONObject();
                    r.put("id", obj.getString("id"));
                    r.put("name", obj.getString("name"));
                    r.put("shouldReceive", shouldReceive);
                    r.put("received", received);
                    r.put("toReceive", toReceive);
                    r.put("ratio", CalculatorUtils.percentage(received, shouldReceive));
                    return r;
                })
                .sorted((a, b) -> {
                    // 收缴率 从大到小排序 为空的放最后
                    BigDecimal ratioA = a.getBigDecimal("ratio");
                    if (ratioA == null) {
                        return 1;
                    }
                    BigDecimal ratioB = b.getBigDecimal("ratio");
                    if (ratioB == null) {
                        return -1;
                    }
                    return ratioB.compareTo(ratioA);
                })
                .collect(Collectors.toList());
    }

    /**
     * 获取筛选的房屋ids
     * 返回null 不筛选
     * 返回空集 无命中结果
     */
    public List<String> getHouseNosFilterList(Token token, JSONObject req) throws Exception {
        if (     // 组织架构
                StringUtil.isBlank(req.getString("departmentId"))
                && StringUtil.isBlank(req.getString("area_names"))
                // 小区名称
                && StringUtil.isBlank(req.getString("communityName"))
                // 房屋信息筛选
                && StringUtil.isBlank(req.getString("floorName"))
                && StringUtil.isBlank(req.getString("buildName"))
                && StringUtil.isBlank(req.getString("unitName"))
                && StringUtil.isBlank(req.getString("houseName"))
        ) {
            return null;
        }

        List<String> departmentIds = basicClient.getDepartmentIdsFilterList(token, req.getString("departmentId"));
        if (Objects.nonNull(departmentIds)) {
            req.put("departmentIds", departmentIds);
        }

        String communityName = req.getString("communityName");
        if(Objects.nonNull(communityName)){
            req.put("name", communityName);
        }

        JSONArray communitys = basicClient.getCommunitys(token, req);
        if (communitys.isEmpty()) {
            return Collections.emptyList();
        }

        final List<String> comIds = communitys.stream()
                .map(item->{
                    JSONObject d = (JSONObject) item;
                    return d.getString("id");
                })
                .collect(Collectors.toList());

        if(Objects.nonNull(comIds)){
            req.put("communityNo", comIds);
        }


        String houseName = req.getString("houseName");
        if(Objects.nonNull(houseName)){
            req.put("name", houseName);
        }
        JSONArray houses = basicClient.getHouses(token, req);

        if(Objects.isNull(houses)){
            return Collections.emptyList();
        }

        return houses.stream().map(item->{
            JSONObject d = (JSONObject) item;
            return d.getString("id");
        }).collect(Collectors.toList());
    }

    /**
     * 判断是否存在与指定账单周期范围重叠的账单
     */
    public boolean existDuplicateBillDateRangeBill(ChargeBill bill) {

        List<ChargeBill> bills = jdbcSession.findArray(ChargeBill.class)
                // 同房屋下
                .eq("houseNo", bill.getHouseNo())
                // 过滤自身
                .noEq("id", bill.getId())
                // 区间存在重合的判断
                .max("billDateTo", bill.getBillDateFrom())
                .min("billDateFrom", bill.getBillDateTo())

                .eq("deleted", false)
                .exe();

        return !bills.isEmpty();
    }

    /**
     * 应收总金额
     */
    public BigDecimal sumTotal(List<ChargeBill> bills) {
        BigDecimal total = bills.stream()
                .map(ChargeBill::getTotal)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        return CalculatorUtils.formatMoney(total);
    }

    /**
     * 已收总金额
     */
    public BigDecimal sumPaidTotal(List<ChargeBill> bills) {
        BigDecimal total = bills.stream()
                .filter(bill -> bill.getPaidState() == BillPaidState.PAID)
                .map(ChargeBill::getTotal)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        return CalculatorUtils.formatMoney(total);
    }

    /**
     * 待收总金额
     */
    public BigDecimal sumUnPaidTotal(List<ChargeBill> bills) {
        BigDecimal total = bills.stream()
                .filter(bill -> bill.getPaidState() == BillPaidState.UNPAID)
                .map(ChargeBill::getTotal)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        return CalculatorUtils.formatMoney(total);
    }

    public void insertChargeRecordRel(String chargeRecordId, ChargeRecordRelType type, String externalId) {
        ChargeRecordRel crr = Pool.newInstance(ChargeRecordRel.class);
        crr.setChargeRecordId(chargeRecordId);
        crr.setType(type);
        crr.setExternalId(externalId);
        jdbcSession.insert(crr);
    }

    public Object batchSaveAnnex(JSONObject req, Token session) {
        JSONArray fileIdsArr = req.getJSONArray("fileIds");
        List<String> fileIds = fileIdsArr.stream()
                .filter(Objects::nonNull)
                .map(String::valueOf)
                .filter(StringUtil::isNotBlank)
                .collect(Collectors.toList());
        if (fileIds.isEmpty()) {
            return Result.error(0, "上传凭证不能为空");
        }

        JSONArray ids = req.getJSONArray("ids");
        if (ids.isEmpty()) {
            return Result.error(0, "请选择账单");
        }

        List<ChargeBill> bills = jdbcSession.findArray(ChargeBill.class)
                .in("id", ids)
                .exe();

        // 数据校验环节
        if (bills.size() != ids.size()) {
            return Result.error(0, "传入了不存在的账单id");
        }
        for (ChargeBill bill : bills) {
            if (bill.getPaidState() != BillPaidState.UNPAID) {
                return Result.error(0, "部分账单当前状态不可操作");
            }
        }

        // 执行环节
        for (ChargeBill bill : bills) {
            chargeBillAnnexService.doInsertBillAnnex(bill.getId(), fileIds, ChargeAnnexType.PAYMENT_VOUCHER);

            bill.setPaidState(BillPaidState.PAID);
            bill.setPaidAt(LocalDateTime.now());
            bill.setPayMethod(BillPayMethod.MANUAL);
            jdbcSession.updateById(bill);

            chargeLogService.saveLog(bill.getId(), BillOperateType.MANUAL_UPDATE_PAID_STATE, req, session.getUsername(), session.getUserId());
        }

        return Result.success();
    }
}
